/*
 * Licensed by the Bunie Developmentgroup.
 */
package infoserver;

import Protocol.*;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.IllegalBlockingModeException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Wouter
 * @since 20-04-2012
 * @version 0.9 The responsibility of this class is processing any incoming
 * packets
 */
public class ServerProcessor extends Thread {

    private InputStream inputStream;
    private OutputStream outputStream;
    private ObjectOutputStream objectOutputStream;
    private ObjectInputStream objectInputStream;
    private final static Logger LOGGER = Logger.getLogger(Log.class.getName());
    private DatabaseConnector databaseConnector = new DatabaseConnector();
    public final static String nullString = "null";
    private Socket socket;
    private boolean threadDone = false;
    private SocketFactory factory;
	
    /**
     * Constructor of the ServerProcessor
     *
     * @param socket Socket to which to listen
     */
	
    public ServerProcessor(Socket socket, SocketFactory sf) {
        print("Hello from ServerProcessor");
        this.socket = socket;
        this.factory = sf;

        outputStream = createOutputStream();
        inputStream = createInputStream();

        objectOutputStream = createObjectOutputStream();
        try {
            objectOutputStream.flush(); // Prevents the client from permanently waiting at new ObjectInputStream();
        } catch (IOException ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        }
        objectInputStream = createObjectInputStream();

        print("Listening");

        while (!threadDone) {
            readObjects();
        }
    }
    
    
     /**
     * Methode for encrypting strings/passwords
     *
     * @param password The string to be encrypted
     * @return Encrypted version of the input
     */
    private static String encrypt(String password) {
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
            byte[] array = md.digest(password.getBytes());
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < array.length; ++i) {
                sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString();
        } catch (java.security.NoSuchAlgorithmException e) {
        }
        return null;
    }

    /**
     * Methode to create inputstream from socket
     *
     * @param socket Socket from which to get the inputstream
     * @return Returns inputstream or null if creating failed
     */
    private InputStream createInputStream() {
        inputStream = null;
        try {
            inputStream = socket.getInputStream();
        } catch (IOException ex) {
            print("Error during inputstream creation");
        } catch (IllegalBlockingModeException ex) {
        }
        return inputStream;
    }

    /**
     * Methode to create outputstream from socket
     *
     * @param socket Socket from which to get the outputstream
     * @return Returns outputstream or null if creating failed
     */
    private OutputStream createOutputStream() {
        outputStream = null;
        try {
            outputStream = socket.getOutputStream();
        } catch (IOException ex) {
            print("Error during outstream creation");
        } catch (IllegalBlockingModeException ex) {
        }
        return outputStream;
    }

    /**
     * Methode to create objectinputstream from an inputstream
     *
     * @param inputStream inputstream for which to create an objectinputstream
     * @return Return the newly created objectinputstream, or null if failed
     */
	
    private ObjectInputStream createObjectInputStream() {
        try {
            objectInputStream = new ObjectInputStream(inputStream);
        } catch (StreamCorruptedException SCE) {
            try {
                objectInputStream.close();
            } catch (IOException ex) {
                Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (SecurityException ex) {
        } catch (NullPointerException ex) {
        } catch (IOException ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return objectInputStream;
    }

    /**
     * Methode to create objectoutputstream from outputstream
     *
     * @param outputStream Outputstream for which to create an
     * objectoutputstream
     * @return Return newly created objectoutputstream or null if failed
     */
    private ObjectOutputStream createObjectOutputStream() {
        try {
            objectOutputStream = new ObjectOutputStream(outputStream);
        } catch (IOException ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
        } catch (NullPointerException ex) {
        }
        return objectOutputStream;
    }

    /**
     * Methode to read Objects using a objectinputstream
     *
     * @param objectInputStream Objectinputstream to use to read objects
     * @return Returns object that was read using objectinputstream
     */
    private Object readObjects() {
        BasicPacket incomingPacket = null;
        try {
            if (!socket.isConnected()) {
                threadDone=true;
               finalize();
               return null;
            }
            Object incomingObject = objectInputStream.readObject();

            if (incomingObject instanceof BasicPacket) {
                incomingPacket = (BasicPacket) incomingObject;
                process(incomingPacket);
            } else {
                System.out.println((String) incomingObject);
            }
            
        } catch (SocketException ex) {
           Thread currentThread = Thread.currentThread();
           currentThread = null;
           threadDone=true;
               finalize();
               return null;             
        } catch (InvalidClassException ex) {
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (StreamCorruptedException ex) {
        } catch (OptionalDataException ex) {
        } catch (EOFException ex) {
            try {
                objectInputStream.close();
                threadDone=true;
                finalize();              
                Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex1) {
                Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex1);
            }
        } catch (IOException ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return incomingPacket;
    }

    /**
     * Methode to write objects using an objectoutputstream
     *
     * @param objectOutputStream Objectoutputstream to use to write object
     * @param objectToWrite Object that must be written to objectoutputstream
     */
    private void writeObjects(ObjectOutputStream objectOutputStream, Object objectToWrite) {
        try {
            objectOutputStream.writeObject(objectToWrite);
            objectOutputStream.flush();
			//objectOutputStream.close();
        } catch (InvalidClassException ex) {
        } catch (NotSerializableException ex) {
		} catch (EOFException ex){
			print("EOF");
        } catch (IOException ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Method to process packets
     *
     * @param packet Packet that needs to be processed
     */
    private void process(BasicPacket packet) {
        switch (packet.getType()) {
            case PacketTypeKeeper.TESTPACKET:
                TestPacket tp = (TestPacket) packet;
                processTestPacket(tp);
                break;
            case PacketTypeKeeper.INLOGREQUESTPACKET:
                InlogRequestPacket ir = (InlogRequestPacket) packet;
                processInlogRequestPacket(ir);
                break;
            case PacketTypeKeeper.ACCOUNTMANAGEMENTREQUESTPACKET:
                AccountManagementRequestPacket amp = (AccountManagementRequestPacket) packet;
                processAccountManagementRequestPacket(amp);
                break;

            case PacketTypeKeeper.QUERYREQUESTPACKET:
                QueryRequestPacket qp = (QueryRequestPacket) packet;
                processQueryPacket(qp);
                break;
            case PacketTypeKeeper.NEWQUERYALERTPACKET:
                for (Socket client : factory.getConnectedSocket()) {
                    try {
                        ObjectOutputStream currentClientOOS = new ObjectOutputStream(client.getOutputStream());
                        NewQueryAlertPacket alertPacket = 
                                (NewQueryAlertPacket)PacketFactory.getPacket(PacketTypeKeeper.NEWQUERYALERTPACKET);
                        writeObjects(currentClientOOS, alertPacket);  
                    } catch (IOException ex) {
                        Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                break;
            default:
                System.out.println(packet);
                break;
        }
        print("Packet was received at Processor: " + packet);
    }

    private void processTestPacket(TestPacket packet) {
        TestPacket testPacket =
                (TestPacket) PacketFactory.getPacket(PacketTypeKeeper.TESTPACKET);
        testPacket.setMessage(packet.getMessage() + "READ");
        writeObjects(objectOutputStream, testPacket);
    }
    /**
     * Method to process the incoming QueryRequestPacket.
     * Executing a Select/Insert/Delete/Update-query
     * @author: Patrick Wobben
     * @since: v0.95
     * @version: v0.99
     * @param packet  The incoming QueryRequestPacket
     */
    private void processQueryPacket(QueryRequestPacket packet) {        
        Log.print(packet.getQuery(), Log.INFO);
        String queryString = packet.getQuery();
        String queryName  = packet.getQueryName();
        int status = packet.getStatus();
        int type = packet.getType();
        int queryId = 1;
        queryId = packet.getQueryId();
        print("Processing query");
    
        switch(status){
			
            case (QueryRequestPacket.SELECT):
                querySelect(packet);
                break;  
            case (QueryRequestPacket.UPDATE):
                queryUpdate(packet);
                break;
				
			case (QueryRequestPacket.DELETE):
                queryDelete(packet);   
                break;
				
			case (QueryRequestPacket.INSERT):
                queryInsert(packet);
                break;
				
			case (QueryRequestPacket.RESEED):
				queryReSeed(packet);
				break;
				
			case (QueryRequestPacket.COLUMN):
				queryColumn(packet);
				break;
        }
    }
    
    /**
     * Method to execute an select-query
     * @author Patrick Wobben
     * @since: v0.99
     * @version: v0.99
     * @param packet Incoming packet
     */
    private void querySelect(QueryRequestPacket packet){
        String queryString = packet.getQuery();
        String queryName  = packet.getQueryName();
        int queryId = 1;
        queryId = packet.getQueryId();
        QueryResponsePacket queryResponsePacket;
        ArrayList<String[]> listWithResults = databaseConnector.executeQuery2(queryString);
		        
		if(databaseConnector.getQueryResult()){
			int results = listWithResults.size();
			
			if (results == 0){
				queryResponsePacket =
                             (QueryResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.QUERYRESPONSEPACKET);
		
				queryResponsePacket.setQueryResult(-1);
				writeObjects(objectOutputStream, queryResponsePacket);
				System.out.println("Sending answer: " + queryResponsePacket);
            } else if (results >= 1) {
				queryResponsePacket =
                             (QueryResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.QUERYRESPONSEPACKET);
				queryResponsePacket.setQueryResult(1);
				queryResponsePacket.setListWithAccounts(listWithResults);
				writeObjects(objectOutputStream, queryResponsePacket);
				System.out.println("Sending answer: " + queryResponsePacket);
				}
		} else {
			queryResponsePacket =
                             (QueryResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.QUERYRESPONSEPACKET);
		
			queryResponsePacket.setQueryResult(-1);
			writeObjects(objectOutputStream, queryResponsePacket);
			System.out.println("The query is wrong. Sending answer: " + queryResponsePacket);
		}
	}
    
    /**
     * Method to execute a query to update something in the database
     * @author: Patrick Wobben
     * @since: v0.95
     * @version v0.95
     * @param packet 
     */
    private void queryUpdate(QueryRequestPacket packet){
        
        String queryString = packet.getQuery();
        String queryName  = packet.getQueryName();
        int queryId = 1;
        queryId = packet.getQueryId();
        QueryResponsePacket queryResponsePacket;
        
        String query = "UPDATE [dbo].BunieQuery SET query_string = '" + queryString + "', query_name = '" + queryName + "' WHERE query_id = " + queryId;
        print(query);
        databaseConnector.executeUpdateQuery(query);
        queryResponsePacket =
                                (QueryResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.QUERYRESPONSEPACKET);
        queryResponsePacket.setQueryResult(1);
        writeObjects(objectOutputStream, queryResponsePacket);
        System.out.println("Sending answer: " + queryResponsePacket);
    }
    
    /**
     * Method to insert something in the database
     * @author: Patrick Wobben
     * @since: v0.95
     * @version: v0.95
     * @param packet 
     */
    private void queryInsert(QueryRequestPacket packet){
        String queryString = packet.getQuery();
        String queryName  = packet.getQueryName();
        int queryId = 1;
        queryId = packet.getQueryId();
        QueryResponsePacket queryResponsePacket;
        String query = "INSERT INTO [dbo].BunieQuery (query_name, query_string) VALUES ('" + queryName + "', '" + queryString + "')";
        print(query);
        databaseConnector.executeInsertQuery(query);
        queryResponsePacket =
                                (QueryResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.QUERYRESPONSEPACKET);
        queryResponsePacket.setQueryResult(1);
        writeObjects(objectOutputStream, queryResponsePacket);
        System.out.println("Sending answer: " + queryResponsePacket);                    
    }
    
    /**
     * Method to delete something in the database
     * @author: Patrick Wobben
     * @since: v0.95
     * @version: v0.95
     * @param packet 
     */
    private void queryDelete(QueryRequestPacket packet){
        String queryString = packet.getQuery();
        QueryResponsePacket queryResponsePacket =
                                (QueryResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.QUERYRESPONSEPACKET);
                queryResponsePacket.setQueryResult(1);
                writeObjects(objectOutputStream, queryResponsePacket);
                databaseConnector.executeDeleteQuery(queryString);
                System.out.println("Sending answer: " + queryResponsePacket); 
    }
    /**
     * Methode to process inlogRequestPackets
     * @param packet inlogRequestPacket that needs processing
     */
    private void processInlogRequestPacket(InlogRequestPacket packet) {
        InlogResponsePacket inlogResponsePacket;

        if (packet.getUsername() != null
                && packet.getUsername().length() > 0) {
        }
        if (packet.getPassword() != null
                && packet.getPassword().length() < 32) {
        }
        if (packet.getTypeOfClient() == PacketTypeKeeper.ADMIN
                || packet.getTypeOfClient() == PacketTypeKeeper.USER) {
        }

        print("processing inlogrequestpacket");

        String query =
                "select * "
                + "from [CRONUS Nederland BV$EP User Login] "
                + "where Name = '" + packet.getUsername() + "'"
                + "AND Password = '" + packet.getPassword() + "'";
        print(query);

        ArrayList<String[]> list = databaseConnector.executeQuery2(query);
        int numberOfRowsReturned = list.size();

        int usertype = 0;
        if (list.size() > 0) {
            String[] row = list.get(0);
            System.out.println("Usertype: " + row[4]);
            usertype = Integer.parseInt(row[4]);
            for (int i = 0; i < row.length; i++) {
                System.out.println("==" + row[i]);
            }
        }

        if (numberOfRowsReturned == 0) {
            System.out.println("Error no account found with those username and password");

            inlogResponsePacket =
                    (InlogResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.INLOGRESPONSEPACKET);
            inlogResponsePacket.setInlogStatus(-1);
            inlogResponsePacket.setTypeOfClient(usertype);
            writeObjects(objectOutputStream, inlogResponsePacket);
        } else if (numberOfRowsReturned == 1) {
            print("You are now logged in");

            inlogResponsePacket =
                    (InlogResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.INLOGRESPONSEPACKET);
            inlogResponsePacket.setInlogStatus(1);
            inlogResponsePacket.setTypeOfClient(usertype);
            writeObjects(objectOutputStream, inlogResponsePacket);

            System.out.println("Incoming packet clienttype was " + usertype);
            System.out.println("Sending answer: " + inlogResponsePacket);
        } else {
            System.out.println("Error multiple matching account returned");

            inlogResponsePacket =
                    (InlogResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.INLOGRESPONSEPACKET);
            inlogResponsePacket.setInlogStatus(-1);
            inlogResponsePacket.setTypeOfClient(usertype);
            writeObjects(objectOutputStream, inlogResponsePacket);
        }
    }

    /**
     * Methode to process AccountManagementRequestPackets
     * @param packet the AccountManagementRequestPacket that needs processing
     */
    private void processAccountManagementRequestPacket(AccountManagementRequestPacket packet) {
        int accountManagementType = packet.getTypeOfManagementPacket();
        System.out.println("Received: " + packet.getTypeOfManagementPacket());

        switch (accountManagementType) {
            case PacketTypeKeeper.INITIAL:
                handleIntial(packet);
                break;
            case PacketTypeKeeper.ADDACCOUNT:
                handleAddAccount(packet);
                break;
            case PacketTypeKeeper.ALTERACCOUNT:
                handleAlterAccount(packet);
                break;
            case PacketTypeKeeper.DELETEACCOUNT:
                handleDeleteAccount(packet);
                break;
            default:
                System.out.println("Something went wrong during the processing of"
                        + "the accountmanagementtype");
        }
    }

    /**
     * Methode to process the accountManagementeRequest type initial packet
     * @param amrp 
     */
    private void handleIntial(AccountManagementRequestPacket amrp) {
        ArrayList<String[]> list = null;

        String query =
                "select [Windows Login ID], Name, Usertype "
                + "from [CRONUS Nederland BV$EP User Login]";

        if (databaseConnector.executeQuery2(query) != null) {
            list = databaseConnector.executeQuery2(query);
        }

        AccountManagementResponsePacket packetToSend =
                (AccountManagementResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.ACCOUNTMANAGEMENTRESPONSEPACKET);

        packetToSend.setResult(list);

        writeObjects(objectOutputStream, packetToSend);
    }

    /**
     * Methode to handle a request to add an account
     * @param amrp 
     */
    private void handleAddAccount(AccountManagementRequestPacket amrp) {
        HashMap<String, String> accountInfo = amrp.getAccountInformation();
        int succesfullAdd=0;

        String query =
                "insert into [CRONUS Nederland BV$EP User Login] "
                + "(Name ,[Windows Login ID], Password, Usertype)"
                + "Values('" + accountInfo.get("username") + "', "
                + "'" + accountInfo.get("windowsLogingID") + "',"
                + "'" + encrypt(accountInfo.get("password")) + "',"
                + "" + accountInfo.get("usertype") + ""
                + ") ";

        succesfullAdd = databaseConnector.executeInsertQuery(query);


        AccountManagementResponsePacket packetToSend =
                (AccountManagementResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.ACCOUNTMANAGEMENTRESPONSEPACKET);
        packetToSend.setSuccesfullAddEditDelete(succesfullAdd);

        writeObjects(objectOutputStream, packetToSend);
    }

    /**
     * Methode to handle a request to alter an account
     * @param amrp 
     */
    private void handleAlterAccount(AccountManagementRequestPacket amrp) {
        HashMap<String, String> accountInfo = amrp.getAccountInformation();
        HashMap<String, String> alterAccount = amrp.getAlterAccountToo();
        
        Iterator<Entry<String, String>> orgiAcc = accountInfo.entrySet().iterator();
        while (orgiAcc.hasNext()) {
            Map.Entry<String, String>entry = orgiAcc.next();
            if (entry.getValue() == null || entry.getValue().equals("")) {
                orgiAcc.remove();
            }
        }
        Iterator<Entry<String, String>> alterTo = alterAccount.entrySet().iterator();
        while (alterTo.hasNext()) {
            Map.Entry<String, String>entry = alterTo.next();
            if (entry.getValue() == null || entry.getValue().equals("")) {
                alterTo.remove();
            }
        }
        
        System.out.println("you where trying to alter this: ");
        System.out.println(accountInfo);
        System.out.println("to this: ");
        System.out.println(alterAccount);
        
        int returnInt = databaseConnector.executeUpdateQuery(accountInfo, alterAccount);

        if (accountInfo.size() < 1 || alterAccount.size()<1) {
            returnInt = -1;
        }
        
        AccountManagementResponsePacket packetToSend =
                (AccountManagementResponsePacket) PacketFactory.getPacket(PacketTypeKeeper.ACCOUNTMANAGEMENTRESPONSEPACKET);
        packetToSend.setSuccesfullAddEditDelete(returnInt);
        System.out.println(returnInt);
        
        writeObjects(objectOutputStream, packetToSend);
    }

    /**
     * Methode to process the where clause of a query
     * @param accountInfo
     * @return 
     */
    private String procesWhere(HashMap<String, String> accountInfo) {
        String where = "";

        if (accountInfo.containsKey("username")) {
            if (accountInfo.get("username") != null) {
                where += "Name = " + "'" + accountInfo.get("username") + "'";
            }
        }
        if (accountInfo.containsKey("windowsLogingID")) {
            if (accountInfo.get("windowsLogingID") != null) {
                if (where.length() > 1) {
                    where += " AND ";
                }
                where += "[Windows Login ID] = " + "'" + accountInfo.get("windowsLogingID") + "'";
            }
        }
        if (accountInfo.containsKey("password")) {
            if (accountInfo.get("password") != null) {
                if (where.length() > 1) {
                    where += " AND ";
                }
                where += "Password = " + "'" + encrypt(accountInfo.get("password")) + "'";
            }
        }
        if (accountInfo.containsKey("usertype")) {
            if (accountInfo.get("usertype") != null) {
                if (where.length() > 1) {
                    where += " AND ";
                }
                where += "Usertype = " + "'" + accountInfo.get("usertype") + "'";
            }
        }
        return where;
    }

    /**
     * Methode to handle a request to delete an account
     * @param amrp 
     */
    private void handleDeleteAccount(AccountManagementRequestPacket amrp) {
        HashMap<String, String> accountInfo = amrp.getAccountInformation();

        String del = procesWhere(accountInfo);

        String query =
                "delete from [CRONUS Nederland BV$EP User Login] "
                + "where " + del;

        print(query);
        int returnedInt = databaseConnector.executeDeleteQuery(query);
        
        AccountManagementResponsePacket packet = new AccountManagementResponsePacket(1);
        packet.setSuccesfullAddEditDelete(returnedInt);
        
        writeObjects(objectOutputStream, packet);
    }

    /**
     * Methode to print String to output
     *
     * @param message String to print
     */
    private void print(String message) {
        System.out.println("Server processor: " + message);
        LOGGER.info(message);
    }
   
    @Override
    public void finalize() {
        try {
            super.finalize();
            
            if (!socket.isInputShutdown()) { 
                socket.shutdownInput();
                for (Socket currentSocket : factory.getConnectedSocket()) {
                    if (currentSocket.equals(socket)) {
                        currentSocket.shutdownInput();
                    }
                }
                print("Input close");
            }
            
            if (!socket.isOutputShutdown()) {
                socket.shutdownOutput();
                for (Socket currentSocket : factory.getConnectedSocket()) {
                    if (currentSocket.equals(socket)) {
                        currentSocket.shutdownOutput();
                    }
                }
                print("Output close");
            }
            
            if (!socket.isClosed() && socket.getKeepAlive()) {
                socket.setKeepAlive(false);
                print("Socket keep alive false");
            } else {
                return;
            }
            
            databaseConnector.closeDatabaseConnector();
            
            if (!socket.isClosed()) {
                for (int i = 0; i < 10; i++) {
                    socket.close();
                    for (Socket currentSock : factory.getConnectedSocket()) {
                        if (currentSock.equals(socket)) {
                            socket.close();
                        }
                    }
                    factory.getConnectedSocket();
                }
            }
            Log.print("Connection closed", Log.INFO);
        } catch (Throwable ex) {
            Logger.getLogger(ServerProcessor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

	/**
	 * Updates query changed status
	 * @author Joep Kemperman
	 * @param packet Incoming packet
	 */
	private void queryReSeed(QueryRequestPacket packet) {
		String seed = packet.getSeed();
		String query = "UPDATE [dbo].[QueryChange] SET [code] = '" + seed + "';";
		databaseConnector.executeUpdateQuery(query);
	}

	/**
	 * Sends a ColumnNamePacket to the client
	 * @param packet Incoming packet
	 */
	private void queryColumn(QueryRequestPacket packet) {
		ColumnNamePacket columnNamePacket = 
				(ColumnNamePacket) PacketFactory.getPacket(PacketTypeKeeper.COLUMNNAMEPACKET);
		Object[] columnNames = databaseConnector.getColumnNames(packet.getQuery());
		columnNamePacket.setColumnNames(columnNames);
		writeObjects(objectOutputStream, columnNamePacket);
	}
}
